package flexcommon.utils
{
	/**
	 * Class containing some static string utility methods.
	 *
	 * <p>Author: Dimitrios Gianninas</p>
	 */
	public class StringUtils
	{

		/**
		 * Abbreviates a String using ellipses.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.abbreviate( null, * ) = null</li>
		 * <li>StringUtils.abbreviate( "", * ) = ""</li>
		 * <li>StringUtils.abbreviate( "abcdefg", 7 ) = "abcd..."</li>
		 * </ul>
		 * 
		 * @param str the string to abbreviate
		 * @param maxLength maximum length of result string
		 * 
		 * @return the abbreviate string
		 */
		public static function abbreviate( str:String, maxLength:Number ):String {
			if( str == null ) {
				return str;
			}
			
			if( str.length > maxLength ) {
				str = str.substring( 0, maxLength - 3 ) + "...";
			}
	
			return str;
		}
    
    /**
     * Capitalizes the string, changing the first character to uppercase.
     * 
     * <p>Examples:</p>
     * 
     * <ul>
     * <li>StringUtils.capitalize( null ) = null</li>
     * <li>StringUtils.capitalize( "" ) = ""</li>
     * <li>StringUtils.capitalize( "hello" ) = "Hello"</li>
     * </ul>
     * 
     * @param str the string to capitalize
     * 
     * @return the capitalized string
     */
    public static function capitalize( str:String ):String
    {
      if( isEmpty( str ) )
      {
        return str;
      }
      else
      {
        var firstChar:String = left( str, 1 );
        return firstChar.toUpperCase() + str.substr( 1 );
      }
    }
		
		/**
		 * Compares two strings (case-insensitive) and returns an integer
		 * representing their natural alphabetical order.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.compare( "Hello World", "Hello World" ) = 0</li>
		 * <li>StringUtils.compare( "Hello World", "A new world" ) = 1</li>
		 * <li>StringUtils.compare( "Hello World", "90210" ) = 1</li>
		 * <li>StringUtils.compare( "Hello World", "Zulu time" ) = -1</li>
		 * </ul>
		 * 
		 * @param s1 the string being compared 
		 * @param s2 the string to compare against
		 *
		 * @return 0 if equal, 1 is s1 is after s2, -1 if s1 is before s2
		 */
		public static function compare( s1:String, s2:String ):Number {
			var str1:String 	= s1.toUpperCase();
			var str2:String 	= s2.toUpperCase();
			var result:Number	= 0;
			var c1:Number		= 0;
			var c2:Number		= 0;
			
			for( var i:Number = 0; i < str1.length; i++ ) {
				c1 = str1.charCodeAt(i);
				c2 = str2.charCodeAt(i);
				
				// if S2 is shorter than S1, then S1 is greater		
				if( isNaN( c2 ) ) {
					result = 1;
					break;
				}
			
				// if we have non-matching characters on the same index,
				// then return which one is greater
				if( c1 != c2 ) {
					result = ( c1 > c2 )? 1: -1;
					break;
				}
				
				// if S1 has ended, then S1 is smaller
				if( (i + 1) == str1.length && str2.length > str1.length ) {
					result = -1;
					break;
				}
			}
			
			return result;
		}
	
		/**
		 * Determines if a string value is contained within the given string.
		 * 
		 * @param s1 the string being evaluated 
		 * @param s2 the string to locate within s1
		 *
		 * @return TRUE if s2 is found within s1, FALSE otherwise
		 */
		public static function contains( s1:String, s2:String ):Boolean {
			return ( !isEmpty( s1 ) && s1.indexOf( s2 ) != -1 );
		}
	
		/**
		 * Counts the number of occurance of string s2 in s1.
		 * 
		 * @param s1 the string being evaluated 
		 * @param s2 the string to locate within s1
		 *
		 * @return the number of occurances of s2 in s1, 0 otherwise
		 */
		public static function count( s1:String, s2:String ):Number {
			if( isEmpty( s1 ) || isEmpty( s2 ) )
			{
				return 0;
			}
			
			var x:Array = s1.split( s2 );
			return x.length - 1;
		}
	
		/**
		 * Returns either the passed in String, or if the String is empty or null, an empty String ("").
		 * 
		 * @param str the string being evaluated 
		 *
		 * @return the string itself, or an empty string if null.
		 */
		public static function defaultString( str:String ):String {
			return ( isEmpty( str ) )? "": str;
		}
    
    /**
     * Determines if a string contains only alphabetic characters.
     * 
     * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.isAlpha( "Hello World" ) = false</li>
		 * <li>StringUtils.isAlpha( "123" ) = false</li>
		 * <li>StringUtils.isAlpha( "Hello" ) = true</li>
		 * <li>StringUtils.isAlpha( null ) = false</li>
     * <li>StringUtils.isAlpha( "" ) = false</li>
		 * </ul>
     * 
     * @param str the string being evaluated 
     *
     * @return TRUE if the string contains only alphabetic characters, FALSE otherwise
     */
    public static function isAlpha( str:String ):Boolean
    {
      if( str == null )
      {
        return false;
      }
      else if( str == "" )
      {
        return true;
      }
      else
      {
        var len:int = str.length;
        
        for( var i:int = 0; i < len; i++ )
        {
          if( !CharacterUtils.isAlphabetic( str.charAt(i) ) )
          {
            return false;
          }
        }
        
        return true;
      }
    }
    
    /**
     * Determines if a string contains only alphabetic characters.
     * 
     * <p>Examples:</p>
     * 
     * <ul>
     * <li>StringUtils.isAlphaNumeric( "Hello World" ) = false</li>
     * <li>StringUtils.isAlphaNumeric( "123" ) = true</li>
     * <li>StringUtils.isAlphaNumeric( "Hello" ) = true</li>
     * <li>StringUtils.isAlphaNumeric( "Hello123" ) = true</li>
     * <li>StringUtils.isAlphaNumeric( null ) = false</li>
     * <li>StringUtils.isAlphaNumeric( "" ) = false</li>
     * </ul>
     * 
     * @param str the string being evaluated 
     *
     * @return TRUE if the string contains only alphabetic characters, FALSE otherwise
     */
    public static function isAlphaNumeric( str:String ):Boolean
    {
      if( str == null )
      {
        return false;
      }
      else if( str == "" )
      {
        return true;
      }
      else
      {
        var len:int = str.length;
        
        for( var i:int = 0; i < len; i++ )
        {
          if( !CharacterUtils.isAlphaNumeric( str.charAt(i) ) )
          {
            return false;
          }
        }
        
        return true;
      }
    }
	
		/**
		 * Determines if a string value is empty. Either null or equal to "".
		 * 
		 * @param str the string being evaluated 
		 *
		 * @return TRUE if the string is empty or null, FALSE otherwise
		 */
		public static function isEmpty( str:String ):Boolean {
			return ( str == null || str.length < 1 || str == "" );
		}
	
		/**
		 * Determines if a string value is not empty.
		 * 
		 * @param str the string being evaluated 
		 *
		 * @return TRUE if the string is not empty or null, FALSE otherwise
		 */
		public static function isNotEmpty( str:String ):Boolean {
			return !isEmpty( str );
		}
    
    /**
     * Determines if a string contains only numeric characters (0-9).
     * 
     * @param str the string being evaluated
     * 
     * @return TRUE if the string contains only numeric characters, FALSE otherwise
     */
    public static function isNumeric( str:String ):Boolean
    {
      if( isEmpty( str ) )
      {
        return false
      }
      else
      {
        return String( Number( str ) ) != "NaN";
      }
    }
    
    /**
     * Returns the lefmost length characters of a string.
     * 
     * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.left( null, 5 ) = null</li>
		 * <li>StringUtils.left( "Hello World", 5 ) = "Hello"</li>
		 * <li>StringUtils.left( "Hello World", 99 ) = ""</li>
		 * </ul>
     * 
     * @param str the string to evaluate
     * @param length the length of characters to return
     * @return the leftmost characters
     */
    public static function left( str:String, length:int ):String
    {
      if( isEmpty( str ) || (length > str.length) )
      {
        return str;
      }
      else if( length <= 0 )
      {
        return "";
      }
      else
      {
        return str.substr( 0, length );
      }
    }
	
		/**
		 * Pads a string with a specified character up to a specified length.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.leftPad( "Hello World", -1 ) = "Hello World"</li>
		 * <li>StringUtils.leftPad( "Hello World", 5 ) = "00000Hello World"</li>
		 * <li>StringUtils.leftPad( "Hello World", 5, "z" ) = "zzzzzHello World"</li>
		 * </ul>
		 * 
		 * @param str the string to pad
		 * @param length the amount of padded to apply
		 * @param char the character to pad, if none specified, pads with "0" 
		 *
		 * @return the padded string
		 */
		public static function leftPad( str:String, length:int, char:String = "0" ):String {
			var padding:String = "";
			
			if( length < 0 )
			{
				return str;
			}
	
			for(var i:int = 0; i < length; i++ ) {
				 padding += char;
			}

			return padding + str;
		}
    
    /**
     * Converts a string to lowercase.
     * 
     * <p>Examples:</p>
     * 
     * <ul>
     * <li>StringUtils.lowercase( null ) = null</li>
     * <li>StringUtils.lowercase( "" ) = ""</li>
     * <li>StringUtils.lowercase( "Hello World" ) = "hello world"</li>
     * </ul>
     * 
     * @param str the string to evaluate
     * @return the string in lowercase
     */
    public static function lowercase( str:String ):String
    {
      if( isEmpty( str ) )
      {
        return str;
      }
      else
      {
        return str.toLowerCase();
      }
    }
	
		/**
		 * Replaces all occurances of a token within a string with another token.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.replaceAll( "Hello World", "World", "Ladies" ) = "Hello Ladies"</li>
		 * <li>StringUtils.replaceAll( "Hello World", "", "" ) = "Hello World"</li>
		 * <li>StringUtils.replaceAll( "Hello World", "Dude", "Ladies" ) = "Hello World"</li>
		 * </ul>
		 * 
		 * @param str the string to evaluate
		 * @param oldToken the token with str to be replaced
		 * @param newToken the token that will replace all occurances of oldToken within the string 
		 *
		 * @return the new string
		 */
		public static function replaceAll( str:String, oldToken:String, newToken:String ):String {
			if( isEmpty( str ) )
			{
				return str;
			}
			
			return str.split( oldToken ).join( newToken );
		}
	
		/**
		 * Replaces the first occurence of a token within a string with another token.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.replaceFirst( "Hello World World", "World", "Ladies" ) = "Hello Ladies World"</li>
		 * <li>StringUtils.replaceFirst( "Hello World", "", "" ) = "Hello World"</li>
		 * <li>StringUtils.replaceFirst( "Hello World", "Dude", "Ladies" ) = "Hello World"</li>
		 * </ul>
		 * 
		 * @param str the string to evaluate
		 * @param oldToken the token with str to be replaced
		 * @param newToken the token that will replace the first occurances of oldToken within the string 
		 *
		 * @return the new string
		 */
		public static function replaceFirst( str:String, oldToken:String, newToken:String ):String {
			var tokenIdx:Number = str.indexOf( oldToken, 0 );
	
			if( tokenIdx == -1 ) {
				return str;
			}
			else {
				return str.substring( 0, tokenIdx )
						+ newToken
						+ str.substring( tokenIdx + oldToken.length );
			}
		}
    
    /**
     * Returns the rightmost length characters of a string.
     * 
     * <p>Examples:</p>
     * 
     * <ul>
     * <li>StringUtils.right( null, 5 ) = null</li>
     * <li>StringUtils.right( *, -5 ) = ""</li>
     * <li>StringUtils.right( "Hello World", 5 ) = "World"</li>
     * <li>StringUtils.right( "Hello World", 99 ) = "Hello World"</li>
     * </ul>
     * 
     * @param str the string to evaluate
     * @param length the length of characters to return
     * @return the rightmost characters
     */
    public static function right( str:String, length:int ):String
    {
      if( isEmpty( str ) || (length > str.length) )
      {
        return str;
      }
      else if( length <= 0 )
      {
        return "";
      }
      else
      {
        return str.substring( (str.length - length) );
      }
    }
	
		/**
		 * Determines if a string s1 starts with the string s2.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.startsWith( "Hello World World", "Hello" ) = true</li>
		 * <li>StringUtils.startsWith( "Hello World", "" ) = false</li>
		 * <li>StringUtils.startsWith( "Hello World", "Dude" ) = false</li>
		 * </ul>
		 * 
		 * @param s1 the string to evaluate
		 * @param s2 the token with str to be replaced
		 *
		 * @return TRUE if s2 was found at the beginning of s1, FALSE otherwise
		 */
		public static function startsWith( s1:String, s2:String ):Boolean {
			if( isEmpty( s1 ) || isEmpty( s2 ) ) {
				return false;
			}
			else if( s1.length < s2.length ) {
				return false;
			}
	
			return ( s1.substr( 0, s2.length ) == s2 );
		}
	
		/**
		 * Gets the substring after the first occurance of a separator.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.substringAfter( "The Force is with you young Skywalker", "you" ) = " young Skywalker"</li>
		 * <li>StringUtils.substringAfter( "The Force is with you young Skywalker", "blue" ) = "The Force is with you young Skywalker"</li>
		 * <li>StringUtils.substringAfter( "The Force is with you young Skywalker", "" ) = "The Force is with you young Skywalker"</li>
		 * </ul>
		 * 
		 * @param str the string to evaluate
		 * @param sep the seperator to locate within the string
		 *
		 * @return the string after the seperator
		 */
		public static function substringAfter( str:String, sep:String ):String {
			var idx:int = str.indexOf( sep );
			
			if( idx == -1 )
			{
				return str;
			}
			
			return str.substring( idx + sep.length );
		}
		
		/**
		 * Gets the substring after the last occurance of a separator.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.substringAfter( "the Force is the rebell alliance", "the" ) = " rebell alliance"</li>
		 * <li>StringUtils.substringAfter( "the Force is the rebell alliance", "blue" ) = "the Force is the rebell alliance"</li>
		 * <li>StringUtils.substringAfter( "the Force is the rebell alliance", "" ) = "the Force is the rebell alliance"</li>
		 * </ul>
		 * 
		 * @param str the string to evaluate
		 * @param sep the seperator to locate within the string
		 *
		 * @return the string after the last occurance of the seperator
		 */		
		public static function substringAfterLast( str:String, sep:String ):String {
			if( isEmpty( sep ) )
			{
				return str;
			}
			
			var idx:int = str.lastIndexOf( sep );
			
			if( idx == -1 )
			{
				return str;
			}
			
			return str.substring( idx + sep.length );
		}
	
		/**
		 * Gets the substring before the first occurance of a separator.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.substringBefore( "the Force is the rebell alliance", "is" ) = "the Force "</li>
		 * <li>StringUtils.substringBefore( "the Force is the rebell alliance", "blue" ) = "the Force is the rebell alliance"</li>
		 * <li>StringUtils.substringBefore( "the Force is the rebell alliance", "" ) = "the Force is the rebell alliance"</li>
		 * </ul>
		 * 
		 * @param str the string to evaluate
		 * @param sep the seperator to locate within the string
		 *
		 * @return the string before the occurance of the seperator
		 */
		public static function substringBefore( str:String, sep:String ):String {
			if( isEmpty( sep ) )
			{
				return str;
			}
			
			var idx:int = str.indexOf( sep );
			
			if( idx == -1 )
			{
				return str;
			}
			
			return str.substring( 0, idx );
		}
	
		/**
		 * Cuts up a string into its various tokens based on the delimiter specified.
		 * The tokens are returned as an array of strings.
		 * 
		 * <p>Examples:</p>
		 * 
		 * <ul>
		 * <li>StringUtils.tokenize( "the Force is the rebell alliance", " " ) = [the,Force,is,the,rebell,alliance]</li>
		 * <li>StringUtils.tokenize( "the Force is the rebell alliance", "blue" ) = []</li>
		 * <li>StringUtils.tokenize( "the Force is the rebell alliance", "" ) = []/li>
		 * </ul>
		 * 
		 * @param str the string to tokenize
		 * @param delim the string to use as a delimiter
		 *
		 * @return an Array with all the tokens
		 */
		public static function tokenize( str:String, delim:String ):Array {
			if( isEmpty( str ) || isEmpty( delim ) )
			{
				return [];
			}
			
			if( str.indexOf( delim ) == -1 )
			{
				return [];
			}
	
			return str.split( delim );
		}
	
		/**
		 * Removes starting and trailing spaces from a string.
		 * 
		 * @param str the string to trim
		 * 
		 * @return the trimmed string
		 */
		public static function trim( str:String ):String {
			if( isEmpty( str ) ) {
				return null;
			}
	
			var startIdx:Number = 0;
			var endIdx:Number = 0;
			var i:Number = 0;
	
			// get start index
			for( i = 0; i < str.length; i++ ) {
				if( str.charAt(i) != " " ) {
					startIdx = i;
					break;
				}
			}
	
			// get end index
			for( i = str.length - 1; i >= 0; i-- ) {
				if( str.charAt(i) != " " ) {
					endIdx = i + 1;
					break;
				}
			}
	
			return ( startIdx == 0 && endIdx == 0 )
					? null
					: str.substring( startIdx, endIdx );
		}
    
    /**
     * Converts a string to uppercase.
     * 
     * <p>Examples:</p>
     * 
     * <ul>
     * <li>StringUtils.uppercase( null ) = null</li>
     * <li>StringUtils.uppercase( "" ) = ""</li>
     * <li>StringUtils.uppercase( "Hello World" ) = "HELLO WORLD"</li>
     * </ul>
     * 
     * @param str the string to evaluate
     * @return the string in uppercase
     */
    public static function uppercase( str:String ):String
    {
      if( isEmpty( str ) )
      {
        return str;
      }
      else
      {
        return str.toUpperCase();
      }
    }
	}
}